package com.cloxtec.scada.service;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.cloxtec.scada.utils.*;
import com.mongodb.BasicDBObject;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.core.Captcha;
import com.ramostear.captcha.support.CaptchaType;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import org.bson.types.ObjectId;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import com.cloxtec.scada.utils.excel.*;
import oshi.SystemInfo;
import oshi.hardware.*;
import oshi.software.os.FileSystem;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

@Component
@Slf4j
public class MqttMsgHandler {

    @Resource
    private ParamHelper paramHelper;

    @Resource
    private LogHelper logHelper;

    @Resource
    private MqttPubClient mqttPubClient;

    @Resource
    private RealTimeData realTimeData;

    @Resource
    private ScheduledTaskManager scheduledTaskManager;

    @Resource
    private UserManager userManager;

    @Resource
    private MongoHelper mongoHelper;

    @Resource
    private ServerInfo serverInfo;

    /**
     * 后台对于配置页面、监控页面订阅的topic：
     * scada_recv_msg
     * 后台发布：
     ***/

    private static final int scada_captcha = 8001;
    private static final int scada_login = 8002;
    private static final int scada_logout = 8003;
    private static final int scada_json = 8004;


    /**
     * 默认算法
     */
    private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding";

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public void init() {
        log.info("mqttMsgHandler ----- init()");
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期


        checkLogMsg();
    }

    public void parseMsg(String topic, JSONObject jsonMsg, int threadId) {
        int msgType = 0;
        if (jsonMsg.containsKey("msg_type")) {
            msgType = jsonMsg.getInteger("msg_type");
        } else if (jsonMsg.containsKey("webMsgType")) {
            msgType = jsonMsg.getInteger("webMsgType");
        } else {
            log.info("非法消息");
            return;
        }

        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
        String strDatetime = sdf.format(date);


        JSONObject jsonLogView = new JSONObject();
        jsonLogView.put("webMsgType", 1025);
        jsonLogView.put("funCode", (msgType < 1000) ? 1 : 2);
        jsonLogView.put("data", new JSONObject());
        jsonLogView.getJSONObject("data").put("datetime", strDatetime);
        jsonLogView.getJSONObject("data").put("target", (msgType < 1000) ? "网关" : "监控");
        jsonLogView.getJSONObject("data").put("direct", "发送");
        jsonLogView.getJSONObject("data").put("topic", topic);
        jsonLogView.getJSONObject("data").put("msgType", msgType);
        jsonLogView.getJSONObject("data").put("msg", paramHelper.isLogMsg() ? jsonMsg : "不显示");

        mqttPubClient.publishMsgEx("yg_scada_service_send_msg", jsonLogView);

        if (!paramHelper.isLogMsg()) {
            log.info("[RECV]-[" + topic + "] msgType=" + msgType);

        } else {
            log.info("[RECV]-[" + topic + "] " + jsonMsg.toJSONString());

        }
        try {
            switch (msgType) {
                case scada_captcha: {// 请求校验码
                    onReqVerifyCode(jsonMsg);
                }
                break;
                case scada_login: {
                    onLogin(jsonMsg);
                }
                break;
                case scada_logout: {
                    onLogout(jsonMsg);
                }
                break;
                case scada_json: {
                    setScada_json(jsonMsg);
                }
                break;
                default:
                    break;
            }

        } catch (Exception e) {
            log.error("异常信息！", e);
            broadcastOperationMsg("收到报文异常");
        }
    }

    private void setScada_json(JSONObject jsonMsg) {
        String clientId = jsonMsg.getString("clientId");
        boolean bLogout = false;
        JSONObject jsonReturn = new JSONObject(jsonMsg);
        JSONObject param = jsonMsg.getJSONObject("data");
        String _id = new String();
        JSONObject key=new JSONObject();
        jsonReturn.put("funCode", jsonMsg.getIntValue("funCode"));
        jsonReturn.put("webMsgType", jsonMsg.getIntValue("webMsgType"));
        int funCode = jsonMsg.getIntValue("funCode");
        boolean addSuccess = false;
        switch (funCode) {
            case 0:
                jsonReturn.put("retCode", 0);
                jsonReturn.put("data", paramHelper.getParam("param_json"));
                break;
            case 1:  // 增加
                // 假设的添加逻辑
                String uuid = UUID.randomUUID().toString();
                param.put("_id", uuid);
                addSuccess = paramHelper.saveParam("param_json", param);
                if (addSuccess) {
                    jsonReturn.put("data", paramHelper.getParam("param_json"));
                    jsonReturn.put("retCode", 0);
                } else {
                    jsonReturn.put("retCode", 1);
                    jsonReturn.put("retMsg", "添加失败，请咨询管理员");
                }
                break;
            case 2:
                // 这里可以添加具体的业务逻辑
                _id = param.getString("_id");
                key = paramHelper.getParamById("param_json", _id);
                if (key != null) {
                    // 假设修改操作，这里需要具体的实现逻辑
                    addSuccess = paramHelper.deleteParam("param_json", _id);
                    if (addSuccess) {
                        jsonReturn.put("data", paramHelper.getParam("param_json"));
                        jsonReturn.put("retCode", 0);
                    } else {
                        jsonReturn.put("retCode", 1);
                        jsonReturn.put("retMsg", "删除失败，请咨询管理员");
                    }
                } else {
                    jsonReturn.put("retCode", 2);
                    jsonReturn.put("retMsg", "没有找到该用户，该用户不存在或请咨询管理员");
                }
                break;
            case 3:  // 修改
                // 修改用户或其他资源的逻辑
                // 假设用户存在且userId不为空
                _id = param.getString("_id");
                key = paramHelper.getParamById("param_json", _id);
                if (key != null) {
                    // 假设修改操作，这里需要具体的实现逻辑
                    // 例如：user.setName(jsonMsg.getString("newName"));
                    addSuccess = paramHelper.updateParam("param_json", param);
                    if (addSuccess) {
                        jsonReturn.put("data", paramHelper.getParam("param_json"));
                        jsonReturn.put("retCode", 0);
                    } else {
                        jsonReturn.put("retCode", 1);
                        jsonReturn.put("retMsg", "添加失败，请咨询管理员");
                    }
                } else {
                    jsonReturn.put("retCode", 2);
                    jsonReturn.put("retMsg", "没有找到该用户，该用户不存在或请咨询管理员");
                }
                break;
            default:
                jsonReturn.put("retCode", 1);
                jsonReturn.put("retMsg", "无效的funCode");
                break;
        }
        mqttPubClient.publishMsg(jsonMsg.getString("clientId"), jsonReturn);

        //        if (userManager.isClientExist(clientId)) {
//            userId = userManager.getClientUsername(clientId);
//            bLogout = userManager.onLogout(clientId);
//            jsonReturn.put("retCode", 0);
//            jsonReturn.put("retMsg", "");
//            userManager.broadcastChangedClients();
//        } else {
//            jsonReturn.put("retCode", 1);
//            jsonReturn.put("retMsg", "当前客户端未登录");
//        }

//        if (bLogout) {
//            JSONObject data = paramHelper.getParamById("param_user", userId);
//            data.remove("_id");
//            data.remove("password");
//            data.remove("nickname");
//            data.remove("cellphone");
//        }
    }

    private void broadcastOperationMsg(String msg) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
        String strDatetime = sdf.format(date);

        JSONObject jsonMsg = new JSONObject();
        jsonMsg.put("webMsgType", 1000);
        jsonMsg.put("funCode", 2);
        jsonMsg.put("data", new JSONObject());
        jsonMsg.getJSONObject("data").put("msg", msg);
        jsonMsg.getJSONObject("data").put("time", strDatetime);
        String topic = "yg_scada_service_send_msg";

        if (!mqttPubClient.publishMsg(topic, jsonMsg)) {
            log.info("publish_msg,topic:[" + topic + "] got an exception:");
        }
    }


    private void onParamService(JSONObject jsonMsg) {
        JSONObject jsonReturn = new JSONObject();
        jsonReturn.put("specialId", jsonMsg.getString("specialId"));
        jsonReturn.put("clientId", jsonMsg.getString("clientId"));
        jsonReturn.put("funCode", jsonMsg.getIntValue("funCode"));
        jsonReturn.put("webMsgType", jsonMsg.getIntValue("webMsgType"));
        jsonReturn.put("retCode", 0);
        jsonReturn.put("retMsg", "");

        int funCode = jsonMsg.getIntValue("funCode");
        switch (funCode) {
            case 3: {
                serverInfo.broadcastSystemInfo();
            }
            break;
            case 5: {
                RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
                // 获取进程ID
                String name = runtimeMXBean.getName();  // 获取进程ID
                long pid = Long.parseLong(name.split("@")[0]);  // 解析进程ID
                restart(jsonReturn, pid);
            }
            break;
        }
    }

    private void onClientOnlineMsg(JSONObject jsonMsg) {
        int funCode = jsonMsg.getIntValue("funCode");
        switch (funCode) {
            case 0: {// 离线了
                if (userManager.isClientExist(jsonMsg.getString("clientId"))) {
                    userManager.setClientOnlineStatus(jsonMsg.getString("clientId"), false);
                }
            }
            break;
            case 1: // 上线了
            case 2: {// 心跳
                if (userManager.isClientExist(jsonMsg.getString("clientId"))) {
                    userManager.resetClientHeartbeat(jsonMsg.getString("clientId"));
                    userManager.setClientOnlineStatus(jsonMsg.getString("clientId"), true);
                }
            }
            break;
        }
    }

    private void onReqVerifyCode(JSONObject jsonMsg) {
        MyCaptcha captcha = new MyCaptcha();
        captcha.setType(CaptchaType.NUMBER);
        captcha.setWidth(160);
        captcha.setHeight(50);
        captcha.setLength(4);
        captcha.setFont(Fonts.getInstance().defaultFont());
        String verifyCode = captcha.getCaptchaCode();
        String strCaptchaImg = captcha.toBase64();

        JSONObject jsonReturn = jsonMsg;

        if (!jsonMsg.containsKey("clientId")) {
            return;
        } else {
            String ClientId = jsonMsg.getString("clientId");
            if (ClientId.length() == 0) {
                return;
            } else {
                if (userManager.isClientLogin(ClientId)) {
                    jsonReturn.put("retCode", 1);
                    jsonReturn.put("retMsg", "已经登录，不需要再次登录");
                } else {
                    userManager.addClient(ClientId);
                    userManager.setClientVerifyCode(jsonMsg.getString("clientId"), verifyCode);
                    userManager.broadcastChangedClients();
                    jsonReturn.put("data", new JSONObject());
                    jsonReturn.getJSONObject("data").put("captchaImg", strCaptchaImg);
                    jsonReturn.put("retCode", 0);
                    jsonReturn.put("retMsg", "");
                }

                mqttPubClient.publishMsg(jsonMsg.getString("clientId"), jsonReturn);
            }
        }
    }

    private void onLogin(JSONObject jsonMsg) {
        JSONObject jsonReturn = new JSONObject();
        jsonReturn.put("webMsgType", jsonMsg.getIntValue("webMsgType"));
        jsonReturn.put("funCode", jsonMsg.getIntValue("funCode"));
        jsonReturn.put("clientId", jsonMsg.getString("clientId"));
        jsonReturn.put("specialId", jsonMsg.getString("specialId"));
        jsonReturn.put("retCode", 0);
        jsonReturn.put("retMsg", "");
        int funCode = jsonMsg.getIntValue("funCode");
        JSONObject user = null;
        String userId = "";
        String password = "";
        String loginVerifyCode = "";
        String username = "";

        JSONObject data = jsonMsg.getJSONObject("data");
        if (data == null) {
            jsonReturn.put("retCode", 1);
            jsonReturn.put("retMsg", "没有登录data信息");
        } else {
            if (data.containsKey("password")) {
                password = data.getString("password");
            }

            if (data.containsKey("verifyCode")) {
                loginVerifyCode = data.getString("verifyCode");
            }

            if (data.containsKey("cellphone")) {
                userId = data.getString("cellphone");
            }

            if (data.containsKey("username")) {
                username = data.getString("username");
            }

            switch (funCode) {
                case 0: {
                    if (username.length() == 0) {
                        jsonReturn.put("retCode", 5);
                        jsonReturn.put("retMsg", "用户名长度为0，不合法");
                    } else {
                        JSONArray users = paramHelper.getParamByKeyValue("param_user", "username", username);
                        if (users.size() == 0) {
                            jsonReturn.put("retCode", 2);
                            jsonReturn.put("retMsg", "没有找到该用户，该用户不存在或找请咨询管理员");
                        } else {
                            user = users.getJSONObject(0);
                            userId = user.getString("id");
                        }
                    }
                }
                break;
                case 1: {
                    if (userId.length() == 0) {
                        jsonReturn.put("retCode", 6);
                        jsonReturn.put("retMsg", "手机号长度为0，不合法");
                    } else {
                        user = paramHelper.getParamById("param_user", userId);
                        if (user == null) {
                            jsonReturn.put("retCode", 2);
                            jsonReturn.put("retMsg", "没有找到该用户，该用户不存在或找请咨询管理员");
                        }
                    }
                }
                break;
            }
        }


        if (jsonReturn.getIntValue("retCode") == 0) {
            String verifyCode = userManager.getClientVerifyCode(jsonMsg.getString("clientId"));
            if (!verifyCode.equals(loginVerifyCode)) {
                jsonReturn.put("retCode", 3);
                jsonReturn.put("retMsg", "校验码错误");
            } else {
                String clientId = jsonMsg.getString("clientId");
                if (!userManager.onLogin(clientId, userId, password, user)) {
                    jsonReturn.put("retCode", 4);
                    jsonReturn.put("retMsg", " 密码错误");
                } else {
                    user.put("userId", user.getString("id"));
                    user.remove("password");
                    user.remove("_id");
                    user.remove("id");
                    jsonReturn.put("data", user);
                    userManager.broadcastChangedClients();
                }
            }
        }

        mqttPubClient.publishMsg(jsonMsg.getString("clientId"), jsonReturn);
    }

    private void onLogout(JSONObject jsonMsg) {
        String clientId = jsonMsg.getString("clientId");
        String userId = "";
        boolean bLogout = false;
        JSONObject jsonReturn = jsonMsg;
        if (userManager.isClientExist(clientId)) {
            userId = userManager.getClientUsername(clientId);
            bLogout = userManager.onLogout(clientId);
            jsonReturn.put("retCode", 0);
            jsonReturn.put("retMsg", "");
            userManager.broadcastChangedClients();
        } else {
            jsonReturn.put("retCode", 1);
            jsonReturn.put("retMsg", "当前客户端未登录");
        }
        mqttPubClient.publishMsg(jsonMsg.getString("clientId"), jsonReturn);

        if (bLogout) {
            JSONObject data = paramHelper.getParamById("param_user", userId);
            data.remove("_id");
            data.remove("password");
            data.remove("nickname");
            data.remove("cellphone");
        }
    }


    private void checkLogMsg() {
        JSONObject param = paramHelper.getParamById("param_system", "log_full_msg");
        if (param == null) {
            paramHelper.setLogMsg(false);
        } else {
            paramHelper.setLogMsg(param.getBooleanValue("value"));
        }
    }


    private void restart(JSONObject jsonReturn, long pid) {
        log.info("【进程id】=" + pid);

        jsonReturn.put("retCode", 0);
        jsonReturn.put("retMsg", "正在执行重启过程");
        mqttPubClient.publishMsg(jsonReturn.getString("clientId"), jsonReturn);

        String cmd = "kill -9 " + pid;
        log.info("【CMD】 " + cmd);
        try {
//            if (FileUtil.isWindows()) {
//                Runtime.getRuntime().exec(cmd);
//            } else {
//                Runtime.getRuntime().exec(cmd);
//            }
            Runtime.getRuntime().exec(cmd);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }

//        System.exit(0);
    }
}

